package lab.cadl.lirui.markdown.core.model

import lab.cadl.lirui.markdown.core.exceptions.MarkdownException
import java.lang.StringBuilder

enum class PaperRefType {
    BOOK,
    BOOK_SECTION,
    CONFERENCE,
    JOURNAL,
    THESIS
}

class PaperRefEntry(val refId: String, val type: PaperRefType) {
    val fields: MutableMap<String, String> = mutableMapOf()

    fun f(name: String): String {
        val value = fields[name]
        if (value == null) {
            val fieldsString = fields.map { "${it.key} = ${it.value}" }.joinToString("\n")
            throw MarkdownException("entry $refId has no $name attribute: $fieldsString")
        } else {
            return unescape(value)
        }
    }

    private fun unescape(s: String): String {
        val sb = StringBuilder()
        var i = 0
        var prevEscape = false
        while (i < s.length) {
            val c = s[i]
            prevEscape = if (c == '\\') {
                if (prevEscape) {
                    sb.append(c)
                    false
                } else {
                    true
                }
            } else {
                sb.append(c)
                false
            }

            i += 1
        }
        return sb.toString()
    }

    val author: String get() = f("author")
    val bookTitle: String get() = f("booktitle")
    val title: String get() = f("title")
    val journal: String get() = f("journal")
    val address: String get() = f("address")
    val chapter: String get() = f("chapter")
    val edition: String get() = f("edition")
    val editor: String get() = f("editor")
    val institution: String get() = f("institution")
    val month: String get() = f("month")
    val number: String get() = f("number")
    val organization: String get() = f("organization")
    val pages: String get() = f("pages").replace(Regex("""\s+"""), "")
    val publisher: String get() = f("publisher")
    val school: String get() = f("school")
    val series: String get() = f("series")
    val volume: String get() = f("volume")
    val year: String get() = f("year")

    fun gbString(): String {
        return when (type) {
            PaperRefType.JOURNAL -> """$author.$title[J].$journal,$year($number):$pages."""
            PaperRefType.CONFERENCE -> """$author.$title[C].$address:$publisher,$year."""
            PaperRefType.BOOK -> """$author.$title[M].$address:$publisher,$year."""
            else -> fields.map { "${it.key} = ${it.value}" }.joinToString(", ")
        }
    }
}

class PaperRefDatabase {
    private val registry: MutableMap<String, PaperRefEntry> = mutableMapOf()

    fun addEntry(entry: PaperRefEntry) {
        registry[entry.refId] = entry
    }

    fun query(refId: String): PaperRefEntry {
        return registry[refId]!!
    }
}

